import java.util.*;
import java.math.*;

public class World {
	Information world[][];
	private short m_playerWithBall;
	private int m_playerWithBallTs;
	
	public World() 
	{
		m_playerWithBall = 0;
		world = new Information[2][13];
		for(int i =0; i < 2; i++) 
		{
			for(int j =0 ; j < 13; j++) 
			{
				world[i][j] = new Information();
			}
		}
	}

	/***
	 * <syntax>
	 * 	void UpdatePlayerWithBallInfo(short playerWithBall, int timestamp)
	 * </syntax>
	 * @param number of the player with the ball
	 * @param timestamp the time in which the agent updates the model
	 * <summary>
	 *	Actualiza los datos de quien tiene el balon y cuando se dio cuenta
	 * </summary>
	 */
	
	public void UpdatePlayerWithBallInfo(short playerWithBall, int timestamp)
	{
		m_playerWithBall = playerWithBall;
		m_playerWithBallTs = timestamp;		
	}
	
	/***
	 * <syntax>
	 * 	public boolean DoesMyTeamHaveTheBall()
	 * </syntax>
	 * <summary>
	 *	Returns a boolean that tells if player's team has possesion of the ball
	 * </summary>
	 */
	public boolean DoesMyTeamHaveTheBall()
	{
		return m_playerWithBall != 0;
	}
	
	/***
	 * <syntax>
	 * 	short GetPlayerWithBall()
	 * </syntax>
	 * <summary>
	 *	Returns the number of current player who has possesion of the ball. Returns 0 if noone has the ball
	 * </summary>
	 */
	public short GetPlayerWithBall()
	{
		return m_playerWithBall;
	}
	
	
	/***
	 * <syntax>
	 * 	void updateAllies(Vector<Element> data, int timestamp)
	 * </syntax>
	 * @param data the vector data to use to update the model
	 * @param timestamp the time in which the agent updates the model
	 * <summary>
	 *	Actualiza los datos de direccion y distancia del agente con respecto a sus aliados
	 * </summary>
	 */
	public void updateAllies(Vector<Member> data, int timestamp) {
		updatePlayersAux(data, timestamp, 0);
	}
	
	/***
	 * <syntax>
	 * 	void updateOponents(Vector<Element> data, int timestamp)
	 * </syntax>
	 * @param data the vector data to use to update the model
	 * @param timestamp the time in which the agent updates the model
	 * <summary>
	 *	Actualiza los datos de direccion y distancia del agente con respecto a sus oponentes
	 * </summary>
	 */
	public void updateOpponents(Vector<Member> data, int timestamp) {
		updatePlayersAux(data, timestamp, 1);
	}
	
	/***
	 * <syntax>
	 * 	void updatePlayersAux(Vector<Element> data, int timestamp, int type)
	 * </syntax>
	 * @param data the vector data to use to update the model
	 * @param timestamp the time in which the agent updates the model
	 * @param type defines the info to update, type=0 for allies, type=1 for opponents
	 * <summary>
	 *	Actualiza los datos de direccion y distancia del agente con respecto a sus aliados
	 * </summary>
	 */
	private void updatePlayersAux(Vector<Member> data, int timestamp, int type) {
		try {
			int dataSize = data.size();
			for(int i = 0; i < dataSize; i++) {
				Member player = data.elementAt(i);
				int numP = player.getNumber() - 1;
				if(player.getType() >= 2) { 
		 			world[type][numP].setDistance(player.getValue("Distance"));
		 			world[type][numP].setDistanceTs(timestamp);
				}
				world[type][numP].setDirection(player.getValue("Direction"));
				world[type][numP].setDirectionTs(timestamp);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/***
	 * <syntax>
	 * 	void updateBall(Vector<Double> ball, int timeStamp)
	 * </syntax>
	 * @param ball the vector data to use to update the ball information
	 * @param timestamp the time in which the agent updates the ball information
	 * <summary>
	 *	Actualiza la información de la pelota, direccion y distancia
	 * </summary>
	 */
	public void updateBall(Vector<Double> ball, int timeStamp) {
		int size = ball.size();
		double direction;
		double distance;
		switch (size) {
			case 0:
				//I don't see the ball
				break;
			case 1:
				direction = ball.elementAt(0);
				world[0][12].setDirection(direction);
				world[0][12].setDirectionTs(timeStamp);
				break;
			default: //TODO, check if we aren't going to need more parameters
				try {
					distance = ball.elementAt(0);
					direction = ball.elementAt(1);
					world[0][12].setDistance(distance);
					world[0][12].setDistanceTs(timeStamp);
					world[0][12].setDirection(direction);
					world[0][12].setDirectionTs(timeStamp);
				} catch (Exception e) {
					//Do something with the exception
				}
				break;
		}
	}
	
	
	/***
	 * <syntax>
	 * 	void updateMyGoal(double x, double y, int timestamp)
	 * </syntax>
	 * <summary>
	 *	Actualiza los datos de direccion y distancia del agente con respecto de su propia meta.
	 * </summary>
	 */	
	public void updateMyGoal(double x, double y, double theta, int timestamp) {
			world[0][11].setDistance(Math.sqrt((x+52)*(x+52)+y*y));
			world[0][11].setDirection(theta + (Math.toDegrees(Math.atan2(-y,(-52-x)))));
	}
	
	/***
	 * <syntax>
	 * 	void updateOpponentGoal(double x, double y, int timestamp )
	 * </syntax>
	 * <summary>
	 *	Actualiza los datos de direccion y distancia del agente con respecto de la meta del oponente.
	 * </summary>
	 */
	public void updateOpponentGoal(double x, double y, double theta, int timestamp) {
			world[1][11].setDistance(Math.sqrt((x-52.5)*(x-52.5)+y*y));
			
			double atan2 = Math.atan2(-y,(52.5-x));
			double toDegrees = Math.toDegrees(atan2);
			world[1][11].setDirection(toDegrees-theta);
//			System.out.println("X: " +" Y: " +y+ " dist: "+world[1][11].distance+" Dir: "+(world[1][11].direction));
	}

	/***
	 * <syntax>
	 * 	void updateGoalAux(Vector<Double> myGoal, int timestamp, int type)
	 * </syntax>
	 * <summary>
	 *	Metodo auxiliar para actualizar los datos de las metas de acuerdo al type
	 *	si type = 0 actualiza my meta
	 *	si type = 1 actualiza a meta oponente 
	 * </summary>
	 */
	private void updateGoalAux(Vector<Double> goal, int timeStamp, int type) {
/*
		int size = goal.size();
		double direction;
		double distance;
		switch (size) {
			case 0:
				//I don't see the goal
				break;
			case 1:
				direction = goal.elementAt(0);
				world[type][11].setDirection(direction);
				world[type][11].setDirectionTs(timeStamp);
				break;
			default: //TODO, check if we aren't going to need more parameters
				distance = goal.elementAt(0);
				direction = goal.elementAt(1);
				world[type][11].setDistance(distance);
				world[type][11].setDistanceTs(timeStamp);
				world[type][11].setDirection(direction);
				world[type][11].setDirectionTs(timeStamp);
				break;
*/
		
	}
	
	/***
	 * <syntax>
	 * 	Information[][] getWorld()
	 * </syntax>
	 * <summary>
	 * 		Regresa la representacion de la informacion del mundo dinamico de cada jugador de la siguiente manera
	 * 		Cada celda esta llena con una instancia de la clase Information, por lo tanto deben de utilisarse, getters de la misma:
	 * 		[
	 * 		[(distanciaAliado, distanciaAliadoTimestamp, direccionAliado, direccionAliadoTimestamp), ...],
	 * 		[(distanciaOponente, distanciaOponenteTimestamp, direccionOponente, direccionOponenteTimestamp), ...]
	 * 		]
	 * </summary>
	 */
	public Information[][] getWorld() {
		return this.world;
	}
	
	//Imprime la informacion dentro del mundo
	public void printWorld() {
		System.out.println("===================>>WORLD MODEL<<===================");
		for(int i =0 ; i < 2; i++) {
			for(int j = 0; j < 13; j++) {
				System.out.println("#" + j + ": " + world[i][j].toString());
			}
			//System.out.println("#" + 12 + ": " + world[i][12].toString());
		}
	}
//Todo Regresaba Distancias...Se cambio a Direccion
	public double getAllyDistance(int num){
		return this.world[0][num-1].distance;}
		
	public double getAllyAngle(int num){
		return this.world[0][num-1].direction;}	
	
	public double getAllyTimestamp(int num){
		return this.world[0][num-1].distanceTs;}
	
	public double getOpponentDistance(int num){
		return this.world[1][num-1].distance;}
		
	public double getOpponentAngle(int num){
		return this.world[1][num-1].direction;}	
	
	public double getOpponentTimestamp(int num){
		return this.world[1][num-1].distanceTs;}	
		
	public double getBallDistance(){
		return world[0][12].distance;}
	
	public double getBallDirection(){
		return world[0][12].direction;}
		
}
